package top.lixunda.commerce.common.core.controller.wrapper;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * @author LiXunda
 * @version time:2019/10/14 11:11
 */
@SuppressWarnings({"WeakerAccess", "unused"})
@Data
@Slf4j
public abstract class BaseDataSqlWrapper<T> {

    private boolean isQuery;

    private DataConditionBlock conditionBlock;

    private OtherBlock otherBlock;

    private EntityBlock<T> entityBlock;

    public boolean isQuery() {
        return isQuery;
    }

    public void isQuery(boolean isQuery) {
        this.isQuery = isQuery;
    }

    static <T> BaseDataSqlWrapper<T> fromWrapper(IAbstractSqlWrapper<T> wrapper, BaseDataSqlWrapper<T> baseDataSqlWrapper) {
        baseDataSqlWrapper.setConditionBlock(wrapper.getConditionBlock().getRoot().toDataConditionBlock());
        baseDataSqlWrapper.setOtherBlock(wrapper.getOtherBlock());
        baseDataSqlWrapper.setEntityBlock(wrapper.getEntityBlock());
        return baseDataSqlWrapper;
    }

    Wrapper<T> toMyBatisWrapper() {
        return BaseDataSqlWrapper.toMyBatisWrapper(this);
    }

    public static <T> AbstractWrapper<T, String, ?> toMyBatisWrapper(BaseDataSqlWrapper<T> baseDataSqlWrapper) {
        AbstractWrapper<T, String, ?> wrapper;
        boolean select = baseDataSqlWrapper.isQuery;
        if (select) {
            wrapper = new QueryWrapper<>();
        } else {
            wrapper = new UpdateWrapper<>();
        }
        //处理otherBlock部分
        OtherBlock otherBlock = baseDataSqlWrapper.getOtherBlock();
        //groupBy
        if (otherBlock.getGroupBy() != null && otherBlock.getGroupBy().size() > 0) {
            wrapper.groupBy(otherBlock.getGroupBy().toArray(new String[]{}));
        }
        //orderBy
        if (otherBlock.getOrderBy() != null && otherBlock.getOrderBy().size() > 0) {
            otherBlock.getOrderBy().forEach((key, value) -> {
                if (OtherBlock.ORDER_BY_DESC.equals(value)) {
                    wrapper.orderByDesc(key);
                } else {
                    wrapper.orderByAsc(key);
                }
            });
        }
        //MyBatisPlus中的Wrapper中不存在limit，limit通过分页插件进行实现，同样这里不进行处理，后面实现时再进行处理
        //todo 暂未处理having操作
        //处理entityBlock部分
        EntityBlock<T> entityBlock = baseDataSqlWrapper.getEntityBlock();
        if (select) {
            switch (entityBlock.getType()) {
                case EntityBlock.TYPE_ALL:
                    //默认全部
                    break;
                case EntityBlock.TYPE_CHOICE:
                    if (entityBlock.getColumns() != null && entityBlock.getColumns().size() > 0) {
                        //noinspection unchecked
                        ((QueryWrapper<T>) wrapper).select(entityBlock.getColumns().keySet().toArray(new String[]{}));
                    } else {
                        log.warn("this wrapper select type is TYPE_CHOICE, but the columns is null or the size is zero");
                        log.warn("set select all");
                    }
                    break;
                case EntityBlock.TYPE_NOT_NULL:
                    break;
                default:
                    throw new IllegalArgumentException("错误的更新查询方式参数");
            }
        } else {
            @SuppressWarnings("unchecked")
            UpdateWrapper<T> tempWrapper = (UpdateWrapper<T>) wrapper;
            switch (entityBlock.getType()) {
                case EntityBlock.TYPE_ALL:
                    //由于MyBatisPlus限制，不为空是通过方法不同进行区分，而不是wrapper，所以这里只能与全部更新相同的做法，
                    // 传递entity，在具体调用MyBatisPlus中service层方法时在进行判断
                case EntityBlock.TYPE_NOT_NULL:
                    //默认全部
                    T t = entityBlock.getEntity();
                    if (t != null) {
                        wrapper.setEntity(t);
                    }
                    break;
                case EntityBlock.TYPE_CHOICE:
                    Map<String, String> columns = entityBlock.getColumns();
                    if (columns != null && columns.size() > 0) {
                        columns.forEach(tempWrapper::set);
                    }
                    break;
                default:
                    throw new IllegalArgumentException("错误的参数");
            }
        }
        //处理condition部分
        doConditionToWrapper(wrapper, baseDataSqlWrapper.getConditionBlock());

        return wrapper;
    }

    /**
     * doConditionToWrapper方法and操作
     */
    private static <T> void doConditionToWrapperAnd(AbstractWrapper<T, String, ?> wrapper, DataConditionBlock condition) {
        //and
        if (condition.getSegs() != null && condition.getSegs().size() > 0) {
            //处理segment and
            String[] values = new String[5];
            int maxParameters = 5;
            condition.getSegs().forEach(segment -> {
                for (int i = 0; i < segment.getValues().size() && i < maxParameters; i++) {
                    values[i] = segment.getValues().get(i);
                }
                switch (segment.getOp()) {
                    case SegmentType.EQ:
                        wrapper.eq(values[0], values[1]);
                        break;
                    case SegmentType.NE:
                        wrapper.ne(values[0], values[1]);
                        break;
                    case SegmentType.GT:
                        wrapper.gt(values[0], values[1]);
                        break;
                    case SegmentType.GE:
                        wrapper.ge(values[0], values[1]);
                        break;
                    case SegmentType.LT:
                        wrapper.lt(values[0], values[1]);
                        break;
                    case SegmentType.LE:
                        wrapper.le(values[0], values[1]);
                        break;
                    case SegmentType.BETWEEN:
                        wrapper.between(values[0], values[1], values[2]);
                        break;
                    case SegmentType.NOT_BETWEEN:
                        wrapper.notBetween(values[0], values[1], values[2]);
                        break;
                    case SegmentType.LIKE:
                        wrapper.like(values[0], values[1]);
                        break;
                    case SegmentType.NOT_LIKE:
                        wrapper.notLike(values[0], values[1]);
                        break;
                    case SegmentType.IS_NULL:
                        wrapper.isNull(values[0]);
                        break;
                    case SegmentType.IS_NOT_NULL:
                        wrapper.isNotNull(values[0]);
                        break;
                    case SegmentType.IN:
                        wrapper.in(values[0], segment.getValues().subList(1, segment.getValues().size()));
                        break;
                    case SegmentType.NOT_IN:
                        wrapper.notIn(values[0], segment.getValues().subList(1, segment.getValues().size()));
                        break;
                    case SegmentType.APPLY:
                        wrapper.apply(values[0], segment.getValues().subList(1, segment.getValues().size()));
                        break;
                    default:
                        throw new IllegalArgumentException("错误的参数");
                }
            });
        }

        //处理子树
        if (condition.getBlocks() != null && condition.getBlocks().size() > 0) {
            condition.getBlocks().forEach(con -> {
                //嵌套
                wrapper.and(itemWrapper -> doConditionToWrapper(itemWrapper, con));
            });
        }
    }

    /**
     * doConditionToWrapper方法or操作
     */
    private static <T> void doConditionToWrapperOr(AbstractWrapper<T, String, ?> wrapper, DataConditionBlock condition) {
        //or
        if (condition.getSegs() != null && condition.getSegs().size() > 0) {
            //处理segment and
            String[] values = new String[5];
            int maxParameters = 5;
            condition.getSegs().forEach(segment -> {
                for (int i = 0; i < segment.getValues().size() && i < maxParameters; i++) {
                    values[i] = segment.getValues().get(i);
                }
                switch (segment.getOp()) {
                    case SegmentType.EQ:
                        wrapper.or().eq(values[0], values[1]);
                        break;
                    case SegmentType.NE:
                        wrapper.or().ne(values[0], values[1]);
                        break;
                    case SegmentType.GT:
                        wrapper.or().gt(values[0], values[1]);
                        break;
                    case SegmentType.GE:
                        wrapper.or().ge(values[0], values[1]);
                        break;
                    case SegmentType.LT:
                        wrapper.or().lt(values[0], values[1]);
                        break;
                    case SegmentType.LE:
                        wrapper.or().le(values[0], values[1]);
                        break;
                    case SegmentType.BETWEEN:
                        wrapper.or().between(values[0], values[1], values[2]);
                        break;
                    case SegmentType.NOT_BETWEEN:
                        wrapper.or().notBetween(values[0], values[1], values[2]);
                        break;
                    case SegmentType.LIKE:
                        wrapper.or().like(values[0], values[1]);
                        break;
                    case SegmentType.NOT_LIKE:
                        wrapper.or().notLike(values[0], values[1]);
                        break;
                    case SegmentType.IS_NULL:
                        wrapper.or().isNull(values[0]);
                        break;
                    case SegmentType.IS_NOT_NULL:
                        wrapper.or().isNotNull(values[0]);
                        break;
                    case SegmentType.IN:
                        wrapper.or().in(values[0], segment.getValues().subList(1, segment.getValues().size()));
                        break;
                    case SegmentType.NOT_IN:
                        wrapper.or().notIn(values[0], segment.getValues().subList(1, segment.getValues().size()));
                        break;
                    case SegmentType.APPLY:
                        List<String> params = Arrays.asList(values)
                                .subList(1, values.length)
                                .stream()
                                .filter(value -> value != null && !"".equals(value))
                                .collect(Collectors.toList());
                        wrapper.apply(values[0], params);
                        break;
                    default:
                        throw new IllegalArgumentException("错误的参数");
                }
            });
        }

        //处理子树
        if (condition.getBlocks() != null && condition.getBlocks().size() > 0) {
            condition.getBlocks().forEach(con -> {
                //嵌套
                wrapper.or(itemWrapper -> doConditionToWrapper(itemWrapper, con));
            });
        }
    }

    private static <T> void doConditionToWrapper(AbstractWrapper<T, String, ?> wrapper, DataConditionBlock condition) {
        if (condition == null) {
            return;
        }
        if (condition.getOp().equals(ConditionBlock.OP_AND)) {
            doConditionToWrapperAnd(wrapper, condition);
        } else {
            doConditionToWrapperOr(wrapper, condition);
        }
    }
}
